<!DOCTYPE html>
<html lang="en">
<head>
  <title>HttpClient - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/httpclient.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/core/httpclient.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>HttpClient</h1>
    <p>Countless services rely on the HTTP-based communication nowadays, and it is a very common application scenario that web applications call back-end HTTP services.</p>
<p>The framework built in <a href="https://github.com/eggjs/egg/blob/master/lib/core/httpclient.js" target="_blank" rel="noopener">HttpClient</a> based on <a href="https://github.com/node-modules/urllib" target="_blank" rel="noopener">urllib</a>, you can quickly complete any HTTP request.</p>
<h2 id="using-httpclient-by-app"><a class="markdown-anchor" href="#using-httpclient-by-app">#</a> Using HttpClient by app</h2>
<p><a href="https://github.com/eggjs/egg/blob/master/lib/core/httpclient.js" target="_blank" rel="noopener">HttpClient</a> will initialize to <code>app.httpclient</code> automatically during the application's initialization.
Also added an method <code>app.curl(url, options)</code>, which is equivalent to the <code>app.httpclient.request(url, options)</code>.</p>
<p>So you can easily use <code>app.curl</code> to complete a HTTP request.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  app.beforeStart(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="comment">// example: read the version info on https://registry.npm.taobao.org/egg/latest when it starts</span></span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> app.curl(<span class="string">'https://registry.npm.taobao.org/egg/latest'</span>, &#123;</span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    app.logger.info(<span class="string">'Egg latest version: %s'</span>, result.data.version);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="using-httpclient-by-context"><a class="markdown-anchor" href="#using-httpclient-by-context">#</a> Using HttpClient by Context</h2>
<p>Framework also provides <code>ctx.curl(url, options)</code> and <code>ctx.httpclient</code> in Context, same as app.
So it's very easy to use <code>ctx.curl()</code> to complete a HTTP request in the Context (such as in the controller)</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> index() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// example: request a npm module's info</span></span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://registry.npm.taobao.org/egg/latest'</span>, &#123;</span><br><span class="line">      <span class="comment">// parse JSON response</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">      <span class="comment">// timeout of 3s</span></span><br><span class="line">      timeout: <span class="number">3000</span>,</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    ctx.body = &#123;</span><br><span class="line">      status: result.status,</span><br><span class="line">      headers: result.headers,</span><br><span class="line">      package: result.data,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="basic-http-request"><a class="markdown-anchor" href="#basic-http-request">#</a> Basic HTTP Request</h2>
<p>HTTP has been widely used and have several methods to make request, but the methods are similar. We start with the basic four request methods then move to some more complex scenario.</p>
<p>In the following example, we will complete the request of https://httpbin.org in the controller.</p>
<h3 id="get"><a class="markdown-anchor" href="#get">#</a> GET</h3>
<p>Reading data almost uses GET request. It is the most common type and widely used in the world of HTTP. And it is also easier to construct a request parameter.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> get() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/get?foo=bar'</span>);</span><br><span class="line">    ctx.status = result.status;</span><br><span class="line">    ctx.set(result.headers);</span><br><span class="line">    ctx.body = result.data;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>GET request might not need to set <code>options.method</code>. HttpClient Defalut method is set to <code>GET</code></li>
<li>Return <code>result</code> will contains 3 attributes: <code>status</code>, <code>headers</code> and <code>data</code>
<ul>
<li><code>status</code>: response status，for example <code>200</code>, <code>302</code>, <code>404</code>, <code>500</code> and etc</li>
<li><code>headers</code>: response header，similar to <code>{ 'content-type': 'text/html', ... }</code></li>
<li><code>data</code>: response body，default HttpClient doesn't do anything and returns as Buffer directly.
Once the <code>options.dataType</code> is set，HttpClient will process the <code>data</code> based on the parameters</li>
</ul>
</li>
</ul>
<p>For the complete request parameter <code>options</code> and return value <code>result</code>, refer to below section <a href="#options-parameters-in-detail">options Parameters in Detail</a></p>
<h3 id="post"><a class="markdown-anchor" href="#post">#</a> POST</h3>
<p>The scenario of creating data generally uses the POST request with body parameter, one more parameter compared to GET.</p>
<p>Take sending JSON boy as example:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> post() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/post'</span>, &#123;</span><br><span class="line">      <span class="comment">// method is required</span></span><br><span class="line">      method: <span class="string">'POST'</span>,</span><br><span class="line">      <span class="comment">// telling HttpClient to send data as JSON by contentType</span></span><br><span class="line">      contentType: <span class="string">'json'</span>,</span><br><span class="line">      data: &#123;</span><br><span class="line">        hello: <span class="string">'world'</span>,</span><br><span class="line">        now: <span class="built_in">Date</span>.now(),</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="comment">// telling HttpClient to process the return body as JSON format explicitly</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.body = result.data;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>The following will explain POST to achieve Form function of form submission and file upload in detail.</p>
<h3 id="put"><a class="markdown-anchor" href="#put">#</a> PUT</h3>
<p>Similar to POST, but PUT is better for data updating and replacement. Almost the same parameters as POST except setting method as <code>PUT</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> put() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/put'</span>, &#123;</span><br><span class="line">      <span class="comment">// method is required</span></span><br><span class="line">      method: <span class="string">'PUT'</span>,</span><br><span class="line">       <span class="comment">// telling HttpClient to send data as JSON by contentType</span></span><br><span class="line">      contentType: <span class="string">'json'</span>,</span><br><span class="line">      data: &#123;</span><br><span class="line">        update: <span class="string">'foo bar'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="comment">// telling HttpClient to process the return body as JSON format explicitly</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.body = result.data;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="delete"><a class="markdown-anchor" href="#delete">#</a> DELETE</h3>
<p>DELETE request is to delete the data, request body don't need to add request body but HttpClient don't have the limitation.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> del() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/delete'</span>, &#123;</span><br><span class="line">      <span class="comment">// method is required</span></span><br><span class="line">      method: <span class="string">'DELETE'</span>,</span><br><span class="line">      <span class="comment">// telling HttpClient to process the return body as JSON format explicitly</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.body = result.data;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="advanced-http-request"><a class="markdown-anchor" href="#advanced-http-request">#</a> Advanced HTTP request</h2>
<p>In some real application scenarios, still have some more complex HTTP requests.</p>
<h3 id="form-submission"><a class="markdown-anchor" href="#form-submission">#</a> Form Submission</h3>
<p>Interfaces of Browser-Oriented Form Submission (without files), usually require <code>content-type: application/x-www-form-urlencoded</code> for the data requesting.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> submit() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/post'</span>, &#123;</span><br><span class="line">      <span class="comment">// method is required, supports POST，PUT and DELETE</span></span><br><span class="line">      method: <span class="string">'POST'</span>,</span><br><span class="line">      <span class="comment">// contentType is not needed, by default HttpClient will send request in application/x-www-form-urlencoded</span></span><br><span class="line">      data: &#123;</span><br><span class="line">        now: <span class="built_in">Date</span>.now(),</span><br><span class="line">        foo: <span class="string">'bar'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="comment">// telling HttpClient to process the return body as JSON format explicitly</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.body = result.data.form;</span><br><span class="line">    <span class="comment">// final response will similar as below:</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//   "foo": "bar",</span></span><br><span class="line">    <span class="comment">//   "now": "1483864184348"</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="uploading-files-by-multipart"><a class="markdown-anchor" href="#uploading-files-by-multipart">#</a> Uploading Files by Multipart</h3>
<p>Once form submission contains files, submission of requesting data must be <a href="http://tools.ietf.org/html/rfc2388" target="_blank" rel="noopener">multipart/form-data</a>
We need to introduce third party module <a href="https://github.com/node-modules/formstream" target="_blank" rel="noopener">formstream</a> to generate <code>form</code> objects that can be consumed by HttpClient.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="keyword">const</span> FormStream = <span class="built_in">require</span>(<span class="string">'formstream'</span>);</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> upload() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="keyword">const</span> form = <span class="keyword">new</span> FormStream();</span><br><span class="line">    <span class="comment">// set normal field and value</span></span><br><span class="line">    form.field(<span class="string">'foo'</span>, <span class="string">'bar'</span>);</span><br><span class="line">    <span class="comment">// uploading the current file for test propose</span></span><br><span class="line">    form.file(<span class="string">'file'</span>, __filename);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(<span class="string">'https://httpbin.org/post'</span>, &#123;</span><br><span class="line">     <span class="comment">// method is required, supports POST，PUT</span></span><br><span class="line">      method: <span class="string">'POST'</span>,</span><br><span class="line">      <span class="comment">// generate request headers following the requirements of multipart/form-data</span></span><br><span class="line">      headers: form.headers(),</span><br><span class="line">      <span class="comment">// submitted as stream mode</span></span><br><span class="line">      stream: form,</span><br><span class="line">      <span class="comment">// telling HttpClient to process the return body as JSON format explicitly</span></span><br><span class="line">      dataType: <span class="string">'json'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.body = result.data.files;</span><br><span class="line">    <span class="comment">// final response will similar as below:</span></span><br><span class="line">    <span class="comment">// &#123;</span></span><br><span class="line">    <span class="comment">//   "file": "'use strict';\n\nconst For...."</span></span><br><span class="line">    <span class="comment">// &#125;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Of course, you can add more files to achieve the requirements of upload multiple files at one time by <code>form.file()</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">form.file(<span class="string">'file1'</span>, file1);</span><br><span class="line">form.file(<span class="string">'file2'</span>, file2);</span><br></pre></td></tr></table></figure>
<h3 id="uploading-files-in-stream-mode"><a class="markdown-anchor" href="#uploading-files-in-stream-mode">#</a> Uploading files in Stream Mode</h3>
<p>In fact, Stream is the leading in the world of Node.js.
If the server supports streaming, the most friendly way is to send the Stream directly. Actually, Stream will be sent in <code>Transfer-Encoding: chunked</code> transmission coding format, which is implemented by <a href="https://nodejs.org/api/http.html" target="_blank" rel="noopener">HTTP</a> module automatically.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/npm.js</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"><span class="keyword">const</span> FormStream = <span class="built_in">require</span>(<span class="string">'formstream'</span>);</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NpmController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> uploadByStream() &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = <span class="keyword">this</span>.ctx;</span><br><span class="line">    <span class="comment">// uploading the current file for test propose</span></span><br><span class="line">    <span class="keyword">const</span> fileStream = fs.createReadStream(__filename);</span><br><span class="line">     <span class="comment">// httpbin.org not support stream mode, use the local stream interface instead</span></span><br><span class="line">    <span class="keyword">const</span> url = <span class="string">`<span class="subst">$&#123;ctx.protocol&#125;</span>://<span class="subst">$&#123;ctx.host&#125;</span>/stream`</span>;</span><br><span class="line">    <span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(url, &#123;</span><br><span class="line">      <span class="comment">// method is required, supports POST，PUT</span></span><br><span class="line">      method: <span class="string">'POST'</span>,</span><br><span class="line">      <span class="comment">// submitted by stream mode</span></span><br><span class="line">      stream: fileStream,</span><br><span class="line">    &#125;);</span><br><span class="line">    ctx.status = result.status;</span><br><span class="line">    ctx.set(result.headers);</span><br><span class="line">    ctx.body = result.data;</span><br><span class="line">    <span class="comment">// final response will similar as below:</span></span><br><span class="line">    <span class="comment">// &#123;"streamSize":574&#125;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="options-parameters-in-detail"><a class="markdown-anchor" href="#options-parameters-in-detail">#</a> options Parameters in Detail</h2>
<p>Due to the complexity of HTTP Request, the options parameters of <code>httpclient.request(url, options)</code> quite large. The actual usage of each optional parameter will be shown with descriptions and coding as below.</p>
<h3 id="default-httpclient-global-configuration"><a class="markdown-anchor" href="#default-httpclient-global-configuration">#</a> Default HttpClient Global Configuration</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/config.default.js</span></span><br><span class="line">exports.httpclient = &#123;</span><br><span class="line">  <span class="comment">// whether to enable local DNS cache, default disable, enable will have two characteristics</span></span><br><span class="line">  <span class="comment">// 1. All DNS lookup will prefer to use the cache by default, even DNS query error does not affects the application</span></span><br><span class="line">  <span class="comment">// 2. For the same hostname, query only once during the interval of dnsCacheLookupInterval (default 10s)</span></span><br><span class="line">  enableDNSCache: <span class="literal">false</span>,</span><br><span class="line">  <span class="comment">// minimum interval of DNS query on the same hostname</span></span><br><span class="line">  dnsCacheLookupInterval: <span class="number">10000</span>,</span><br><span class="line">  <span class="comment">// maximum number of hostname DNS cache simultaneously, default 1000</span></span><br><span class="line">  dnsCacheMaxLength: <span class="number">1000</span>,</span><br><span class="line"></span><br><span class="line">  request: &#123;</span><br><span class="line">    <span class="comment">// default timeout of request</span></span><br><span class="line">    timeout: <span class="number">3000</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  httpAgent: &#123;</span><br><span class="line">    <span class="comment">// default enable http KeepAlive</span></span><br><span class="line">    keepAlive: <span class="literal">true</span>,</span><br><span class="line">    <span class="comment">// idle KeepAlive socket can survive for 4 seconds</span></span><br><span class="line">    freeSocketKeepAliveTimeout: <span class="number">4000</span>,</span><br><span class="line">    <span class="comment">// when sockets have no activity for more than 30s, it will be processed as timeout</span></span><br><span class="line">    timeout: <span class="number">30000</span>,</span><br><span class="line">    <span class="comment">// maximum number of sockets allow to be created</span></span><br><span class="line">    maxSockets: <span class="built_in">Number</span>.MAX_SAFE_INTEGER,</span><br><span class="line">    <span class="comment">// maximum number of idle sockets</span></span><br><span class="line">    maxFreeSockets: <span class="number">256</span>,</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  httpsAgent: &#123;</span><br><span class="line">    <span class="comment">// default enable https KeepAlive</span></span><br><span class="line">    keepAlive: <span class="literal">true</span>,</span><br><span class="line">    <span class="comment">// idle KeepAlive socket can survive for 4 seconds</span></span><br><span class="line">    freeSocketKeepAliveTimeout: <span class="number">4000</span>,</span><br><span class="line">    <span class="comment">// when sockets have no activity for more than 30s, it will be processed as timeout</span></span><br><span class="line">    timeout: <span class="number">30000</span>,</span><br><span class="line">    <span class="comment">// maximum number of sockets allow to be created</span></span><br><span class="line">    maxSockets: <span class="built_in">Number</span>.MAX_SAFE_INTEGER,</span><br><span class="line">    <span class="comment">// maximum number of idle sockets</span></span><br><span class="line">    maxFreeSockets: <span class="number">256</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Application can overrides the configuration by <code>config/config.default.js</code></p>
<h3 id="data-object"><a class="markdown-anchor" href="#data-object">#</a> <code>data: Object</code></h3>
<p>The request data will select the correct processing method automatically based on the <code>method</code>.</p>
<ul>
<li>GET，HEAD: processed by <code>querystring.stringify(data)</code> then append to the query parameters of url.</li>
<li>POST，PUT, DELETE and etc: further judgments and process according to <code>contentType</code>.
<ul>
<li><code>contentType = json</code>: processed by <code>JSON.stringify(data)</code> and set it as body before sending.</li>
<li>others: processed by <code>querystring.stringify(data)</code> and set it as body before sending</li>
</ul>
</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// GET + data</span></span><br><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  data: &#123; <span class="attr">foo</span>: <span class="string">'bar'</span> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// POST + data</span></span><br><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  data: &#123; <span class="attr">foo</span>: <span class="string">'bar'</span> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// POST + JSON + data</span></span><br><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  contentType: <span class="string">'json'</span>,</span><br><span class="line">  data: &#123; <span class="attr">foo</span>: <span class="string">'bar'</span> &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="dataasquerystring-boolean"><a class="markdown-anchor" href="#dataasquerystring-boolean">#</a> <code>dataAsQueryString: Boolean</code></h3>
<p>Once <code>dataAsQueryString=true</code> is set, even under POST, it will forces <code>options.data</code> to be processed by <code>querystring.stringify</code> then append to the <code>url</code> query parameters</p>
<p>The application scenarios that sending data using <code>stream</code> and pass additional request parameters by <code>url</code> query can be well resolved.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  dataAsQueryString: <span class="literal">true</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    <span class="comment">// generally it would be some validation parameters such as access token, etc.</span></span><br><span class="line">    accessToken: <span class="string">'some access token value'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">  stream: myFileStream,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="content-stringbuffer"><a class="markdown-anchor" href="#content-stringbuffer">#</a> <code>content: String|Buffer</code></h3>
<p>Set request Context, if the parameter is set, it will ignore the <code>data</code> parameters</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  <span class="comment">// Sending the raw xml data without HttpClient's to do processing</span></span><br><span class="line">  content: <span class="string">'&lt;xml&gt;&lt;hello&gt;world&lt;/hello&gt;&lt;/xml&gt;'</span>,</span><br><span class="line">  headers: &#123;</span><br><span class="line">    <span class="string">'content-type'</span>: <span class="string">'text/html'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="stream-readstream"><a class="markdown-anchor" href="#stream-readstream">#</a> <code>stream: ReadStream</code></h3>
<p>Set request context's readable stream, default <code>null</code>.
If the parameter is set , HttpClient will ignore <code>data</code> and <code>content</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  stream: fs.createReadStream(<span class="string">'/path/to/read'</span>),</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="writestream-writestream"><a class="markdown-anchor" href="#writestream-writestream">#</a> <code>writeStream: WriteStream</code></h3>
<p>Set receive response data's writeable stream, default <code>null</code>.
Once the parameter is set, response <code>result.data</code> is set to <code>null</code> because all data are written to <code>writeStream</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  writeStream: fs.createWriteStream(<span class="string">'/path/to/store'</span>),</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="consumewritestream-boolean"><a class="markdown-anchor" href="#consumewritestream-boolean">#</a> <code>consumeWriteStream: Boolean</code></h3>
<p>Whether to wait for <code>writeStream</code> completely finished as the response well received
This parameter is not recommended to modify the default value, unless we know it's side effect are acceptable. Otherwise, the <code>writeStream</code> data is likely to be incomplete.</p>
<h3 id="method-string"><a class="markdown-anchor" href="#method-string">#</a> <code>method: String</code></h3>
<p>Set request method, default <code>GET</code>. Support all <code>GET、POST、PUT、DELETE、PATCH</code> and so on <a href="https://nodejs.org/api/http.html#http_http_methods" target="_blank" rel="noopener">all HTTP methods</a>。</p>
<h3 id="contenttype-string"><a class="markdown-anchor" href="#contenttype-string">#</a> <code>contentType: String</code></h3>
<p>Set request data format ，default <code>undefined</code>，HttpClient will sets automatically based on the <code>data</code> and <code>content</code> parameters.
When <code>data</code> is object, the default setting would be <code>form</code>. Support <code>json</code> format.</p>
<p>If need to send <code>data</code> by JSON</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  method: <span class="string">'POST'</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    foo: <span class="string">'bar'</span>,</span><br><span class="line">    now: <span class="built_in">Date</span>.now(),</span><br><span class="line">  &#125;,</span><br><span class="line">  contentType: <span class="string">'json'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="datatype-string"><a class="markdown-anchor" href="#datatype-string">#</a> <code>dataType: String</code></h3>
<p>Set the response data format, default return the raw buffer formatted data without processing. Support <code>text</code> and <code>json</code></p>
<p><strong>Note: If <code>json</code> is set，a <code>JSONResponseFormatError</code>  error would be thrown if fails to parse the response data.</strong></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> jsonResult = <span class="keyword">await</span> ctx.curl(url, &#123;</span><br><span class="line">  dataType: <span class="string">'json'</span>,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">console</span>.log(jsonResult.data);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> htmlResult = <span class="keyword">await</span> ctx.curl(url, &#123;</span><br><span class="line">  dataType: <span class="string">'text'</span>,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">console</span>.log(htmlResult.data);</span><br></pre></td></tr></table></figure>
<h3 id="fixjsonctlchars-boolean"><a class="markdown-anchor" href="#fixjsonctlchars-boolean">#</a> <code>fixJSONCtlChars: Boolean</code></h3>
<p>Whether filter the special control characters in the response data (U+0000 ~ U+001F)，default <code>false</code>
Typically, the JSON data returned by some CGI system might contains such special control characters, which can be filter automatically by setting the parameters.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  fixJSONCtlChars: <span class="literal">true</span>,</span><br><span class="line">  dataType: <span class="string">'json'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="headers-object"><a class="markdown-anchor" href="#headers-object">#</a> <code>headers: Object</code></h3>
<p>Custom request headers</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  headers: &#123;</span><br><span class="line">    <span class="string">'x-foo'</span>: <span class="string">'bar'</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="timeout-numberarray"><a class="markdown-anchor" href="#timeout-numberarray">#</a> <code>timeout: Number|Array</code></h3>
<p>Timeout of request, default <code>[ 5000, 5000 ]</code>, timeout of connection creation is 5s, and the timeout of receive response is 5s.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  <span class="comment">// 3s timeout of connection creation, and the 3s timeout of receive response</span></span><br><span class="line">  timeout: <span class="number">3000</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  <span class="comment">// 1s timeout of connection creation, and the 30s timeout of receive response for the responsing of larger scenarios</span></span><br><span class="line">  timeout: [ <span class="number">1000</span>, <span class="number">30000</span> ],</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="agent-httpagent"><a class="markdown-anchor" href="#agent-httpagent">#</a> <code>agent: HttpAgent</code></h3>
<p>Allows to override the default HttpAgent through this parameter. If you don't want to enable KeepAlive, set this parameter to <code>false</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  agent: <span class="literal">false</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="httpsagent-httpsagent"><a class="markdown-anchor" href="#httpsagent-httpsagent">#</a> <code>httpsAgent: HttpsAgent</code></h3>
<p>Allows to override the default HttpsAgent through this parameter. If you don't want to enable KeepAlive, set this parameter to <code>false</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  httpsAgent: <span class="literal">false</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="auth-string"><a class="markdown-anchor" href="#auth-string">#</a> <code>auth: String</code></h3>
<p>Parameter of Simple login authorization (Basic Authentication), will send the login information to the <code>Authorization</code> request in clear form.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  <span class="comment">// parameter must follow the format of `user:password`</span></span><br><span class="line">  auth: <span class="string">'foo:bar'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="digestauth-string"><a class="markdown-anchor" href="#digestauth-string">#</a> <code>digestAuth: String</code></h3>
<p>Parameter of the Digest Authentication. If the parameter is set, it will attempt to generate the <code>Authorization</code> request header for the 401 response automatically then try requesting for authorization once.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  <span class="comment">// parameter must follow the format of `user:password`</span></span><br><span class="line">  digestAuth: <span class="string">'foo:bar'</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="followredirect-boolean"><a class="markdown-anchor" href="#followredirect-boolean">#</a> <code>followRedirect: Boolean</code></h3>
<p>Whether to follow 3xx redirect response, default <code>false</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  followRedirect: <span class="literal">true</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="maxredirects-number"><a class="markdown-anchor" href="#maxredirects-number">#</a> <code>maxRedirects: Number</code></h3>
<p>Set the maximum number of automatic redirects to prevent the endless redirect loop, default 10 times.
The parameter should not be set too large and only works in the <code>followRedirect=true</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  followRedirect: <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// maximum allowed redirect 5 times</span></span><br><span class="line">  maxRedirects: <span class="number">5</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="formatredirecturl-functionfrom-to"><a class="markdown-anchor" href="#formatredirecturl-functionfrom-to">#</a> <code>formatRedirectUrl: Function(from, to)</code></h3>
<p><code>formatRedirectUrl</code> allow us to customize the implementation of 302、301 other redirect URL splicing, default <code>url.resolve (from, to)</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  formatRedirectUrl: <span class="function">(<span class="params"><span class="keyword">from</span>, to</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// for example you can correct the redirection of wrong url here</span></span><br><span class="line">    <span class="keyword">if</span> (to === <span class="string">'//foo/'</span>) &#123;</span><br><span class="line">      to = <span class="string">'/foo'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> url.resolve(<span class="keyword">from</span>, to);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="beforerequest-functionoptions"><a class="markdown-anchor" href="#beforerequest-functionoptions">#</a> <code>beforeRequest: Function(options)</code></h3>
<p>HttpClient will attempt to invoke the <code>beforeRequest</code> hook before requesting officially, allowing us to make the last modification of the request parameter here.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  beforeRequest: <span class="function"><span class="params">options</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// For example, we can set the global request ID to facilitate log tracking</span></span><br><span class="line">    options.headers[<span class="string">'x-request-id'</span>] = uuid.v1();</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="streaming-boolean"><a class="markdown-anchor" href="#streaming-boolean">#</a> <code>streaming: Boolean</code></h3>
<p>Whether to return the response stream directly, default <code>false</code>
After enable streaming, HttpClient will return immediately after getting the response object res,
At this moment <code>result.headers</code> and <code>result.status</code> can be read, but still cannot read the data</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(url, &#123;</span><br><span class="line">  streaming: <span class="literal">true</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(result.status, result.data);</span><br><span class="line"><span class="comment">// result.res is a ReadStream Object</span></span><br><span class="line">ctx.body = result.res;</span><br></pre></td></tr></table></figure>
<p><strong>if res is not passed to body directly, then we must consume this stream and do well in error handling.</strong></p>
<h3 id="gzip-boolean"><a class="markdown-anchor" href="#gzip-boolean">#</a> <code>gzip: Boolean</code></h3>
<p>Whether to support gzip response format, default <code>false</code>
After enable gzip, HttpClient will set <code>Accept-Encoding: gzip</code> header and extract the data with <code>Content-Encoding: gzip</code> response header automatically.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.curl(url, &#123;</span><br><span class="line">  gzip: <span class="literal">true</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="timing-boolean"><a class="markdown-anchor" href="#timing-boolean">#</a> <code>timing: Boolean</code></h3>
<p>Whether to enable the time measurement for each phase, default <code>false</code>
After enable the timing, you can get the time measurements of HTTP request (in milliseconds) from the <code>result.res.timing</code>.
Through these measurements, we can easily locate the slowest environment in the request, similar to the Chrome network timing.</p>
<p>Measurement timing's analysis of each stage:</p>
<ul>
<li>queuing: allocating socket time consuming</li>
<li>dnslookup: DNS queries time consuming</li>
<li>connected: socket three handshake success time consuming</li>
<li>requestSent: requesting full data time consuming</li>
<li>waiting: first byte to received response time consuming</li>
<li>contentDownload: full response data time consuming</li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> result = <span class="keyword">await</span> ctx.curl(url, &#123;</span><br><span class="line">  timing: <span class="literal">true</span>,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">console</span>.log(result.res.timing);</span><br><span class="line"><span class="comment">// &#123;</span></span><br><span class="line"><span class="comment">//   "queuing":29,</span></span><br><span class="line"><span class="comment">//   "dnslookup":37,</span></span><br><span class="line"><span class="comment">//   "connected":370,</span></span><br><span class="line"><span class="comment">//   "requestSent":1001,</span></span><br><span class="line"><span class="comment">//   "waiting":1833,</span></span><br><span class="line"><span class="comment">//   "contentDownload":3416</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br></pre></td></tr></table></figure>
<h3 id="carejectunauthorizedpfxkeycertpassphrasecipherssecureprotocol"><a class="markdown-anchor" href="#carejectunauthorizedpfxkeycertpassphrasecipherssecureprotocol">#</a> <code>ca，rejectUnauthorized，pfx，key，cert，passphrase，ciphers，secureProtocol</code></h3>
<p>These are parameters are passed to the  <a href="https://nodejs.org/api/https.html" target="_blank" rel="noopener">HTTPS</a> modules，details refer to <a href="https://nodejs.org/api/https.html#https_https_request_options_callback" target="_blank" rel="noopener"><code>https.request(options, callback)</code></a>。</p>
<h2 id="debugging-aid"><a class="markdown-anchor" href="#debugging-aid">#</a> Debugging Aid</h2>
<p>Framework provides <a href="https://github.com/eggjs/egg-development-proxyagent" target="_blank" rel="noopener">egg-development-proxyagent</a> plugin to help developers to debug.</p>
<p>Install and enable pulgin:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm i egg-development-proxyagent --save</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// config/plugin.js</span></span><br><span class="line">exports.proxyagent = &#123;</span><br><span class="line">  enable: <span class="literal">true</span>,</span><br><span class="line">  package: <span class="string">'egg-development-proxyagent'</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Open capture tools, we can use <a href="https://www.charlesproxy.com/" target="_blank" rel="noopener">charles</a> or <a href="http://www.telerik.com/fiddler" target="_blank" rel="noopener">fiddler</a>, here we take to <a href="https://github.com/alibaba/anyproxy" target="_blank" rel="noopener">anyproxy</a> demonstrate</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ npm install anyproxy -g</span><br><span class="line">$ anyproxy --port 8888</span><br></pre></td></tr></table></figure>
<p>Starting application using environment variables:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ http_proxy=http://127.0.0.1:8888 npm run dev</span><br></pre></td></tr></table></figure>
<p>Then it works correctly, and all requests that go through HttpClient can be viewed in the consle of http://localhost:8002.</p>
<p><img src="https://cloud.githubusercontent.com/assets/227713/21976937/06a63694-dc0f-11e6-98b5-e9e279c4867c.png" alt="anyproxy"></p>
<p><strong>Note: the pulgin only start in local environments by defalut</strong></p>
<h2 id="known-issues"><a class="markdown-anchor" href="#known-issues">#</a> Known issues</h2>
<h3 id="connection-timeout"><a class="markdown-anchor" href="#connection-timeout">#</a> Connection Timeout</h3>
<ul>
<li>Exception: <code>ConnectionTimeoutError</code></li>
<li>Scene: usually occurred by the DNS query is slow, or the network is slow between the client and server</li>
<li>Troubleshooting Suggestion: increase the <code>timeout</code> parameter appropriately.</li>
</ul>
<h3 id="service-response-timeout"><a class="markdown-anchor" href="#service-response-timeout">#</a> Service Response Timeout</h3>
<ul>
<li>Exception: <code>ResponseTimeoutError</code></li>
<li>Scene: usually occurred by network is slower between the client and server, and happens when the data is relatively large.</li>
<li>Troubleshooting Suggestion: increase the <code>timeout</code> parameter appropriately.</li>
</ul>
<h3 id="service-disconnect"><a class="markdown-anchor" href="#service-disconnect">#</a> Service Disconnect</h3>
<ul>
<li>Exception: <code>ResponseError, code: ECONNRESET</code></li>
<li>Scene: usually the server actively disconnects the socket connection, causing the HTTP request link exceptions.</li>
<li>Troubleshooting Suggestion: please check if server has network exception at that time</li>
</ul>
<h3 id="service-is-unreachable"><a class="markdown-anchor" href="#service-is-unreachable">#</a> Service is unreachable</h3>
<ul>
<li>Exception: <code>RequestError, code: ECONNREFUSED, status: -1</code></li>
<li>Scene: usually because the requested URL which attached IP or the port cannot connect successfully.</li>
<li>Troubleshooting Suggestion: make sure the IP or port is set correctly</li>
</ul>
<h3 id="domain-name-is-not-existing"><a class="markdown-anchor" href="#domain-name-is-not-existing">#</a> Domain name is not existing</h3>
<ul>
<li>Exception: <code>RequestError, code: ENOTFOUND, status: -1</code></li>
<li>Scene: usually the domain name requested by URL cannot be resolved by DNS successfully.</li>
<li>Troubleshooting Suggestion: make sure the domain name exists, and also check to see if the DNS service is properly configured.</li>
</ul>
<h3 id="json-response-data-format-error"><a class="markdown-anchor" href="#json-response-data-format-error">#</a> JSON Response data format error</h3>
<ul>
<li>Exception: <code>JSONResponseFormatError</code></li>
<li>scene: the <code>dataType=json</code> is set and this exception is thrown in response data that does not match JSON format.</li>
<li>Troubleshooting Suggestion: make sure that the server no matter what situations are returns the data in JSON format correctly.</li>
</ul>
<h2 id="global-request-and-response-events"><a class="markdown-anchor" href="#global-request-and-response-events">#</a> Global <code>request</code> and <code>response</code> events</h2>
<p>In enterprise application scenarios, generally a unified tracer log is needed.
To facilitate monitoring HttpClient requests and responses on the app level, we agreed on global <code>request</code> and <code>response</code> to expose these two events.</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">init options</span><br><span class="line">    |</span><br><span class="line">    V</span><br><span class="line">emit `request` event</span><br><span class="line">    |</span><br><span class="line">    V</span><br><span class="line">send request and receive response</span><br><span class="line">    |</span><br><span class="line">    V</span><br><span class="line">emit `response` event</span><br><span class="line">    |</span><br><span class="line">    V</span><br><span class="line">   end</span><br></pre></td></tr></table></figure>
<h3 id="request-event-occurs-before-the-network-operation"><a class="markdown-anchor" href="#request-event-occurs-before-the-network-operation">#</a> <code>request</code> event occurs before the network operation</h3>
<p>A <code>request</code> event is triggered before the request is sent, allowing blocking of the request.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.httpclient.on(<span class="string">'request'</span>, req =&gt; &#123;</span><br><span class="line">  req.url <span class="comment">//request url</span></span><br><span class="line">  req.ctx <span class="comment">//context of the request</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// you can set some trace headers here for full link tracking propose</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="response-event-occurs-after-the-end-of-network-operation"><a class="markdown-anchor" href="#response-event-occurs-after-the-end-of-network-operation">#</a> <code>response</code> event occurs after the end of network operation</h3>
<p>After the end of request, a <code>response</code> event is triggered, so that the external event can be subscribed to the log printing.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.httpclient.on(<span class="string">'response'</span>, result =&gt; &#123;</span><br><span class="line">  result.res.status</span><br><span class="line">  result.ctx <span class="comment">//context of the request</span></span><br><span class="line">  result.req <span class="comment">//the corresponding req object, which the req in the request event</span></span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="example"><a class="markdown-anchor" href="#example">#</a> Example</h2>
<p>Full examples can be found on <a href="https://github.com/eggjs/examples/blob/master/httpclient" target="_blank" rel="noopener">eggjs/exmaples/httpclient</a> .</p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/httpclient.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/core/httpclient.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
